package org.smartboot.compare;

/**
 * @author qinluo
 * @date 2024-03-18 22:54:47
 * @since 1.0.7
 */
public enum Option {

    /**
     * Loose mode.
     *
     * It's means expect equals actual even their value are different.
     *
     * example:
     *
     * In loose mode, empty list and null is equals.
     * <pre>
     *     {@code
     *         List<Integer> expect = Collections.emptyList();
     *         List<Integer> actual = null;
     *
     *         CompareResult result = CompareHelper.compare(expect, actual, Option.mix(LOOSE_MODE));
     *         // result is same.
     *         assert result.isSame();
     *     }
     * </pre>
     *
     */
    LOOSE_MODE,

    /**
     * Disable customized equals methods.
     */
    DISABLE_EQUALS,

    /**
     * Immediately interrupt when compare process failed.
     */
    IMMEDIATELY_INTERRUPT,

    /**
     * Beautiful array difference result.
     */
    BEAUTIFUL_ARRAY_RESULT,

    /**
     * Transfer List/Array as Set.
     */
    TRANS_AS_SET,

    /**
     * Use expect-value's type in Map when expect-value's type not equals or subclass of compare-value's type.
     */
    USE_EXPECT_TYPE_IN_MAP,

    ;

    /**
     * Option bits.
     */
    private final long bits = 1L << this.ordinal();

    public long getBits() {
        return bits;
    }

    /**
     * Mix option bits as long.
     *
     * @param option  Main option.
     * @param options Additional options.
     * @return        Options bits
     */
    public static long mix(Option option, Option... options) {
        long v = option.bits;

        if (options != null) {
            for (Option op : options) {
                v |= op.bits;
            }
        }

        return v;
    }

    /**
     * Serialize long options as String.
     *
     * @param options long options.
     * @return        serialized-string.
     */
    public static String serialize(long options) {
        StringBuilder sb = new StringBuilder();

        for (Option option : values()) {
            if ((option.bits & options) == option.bits) {
                sb.append(option.name()).append(", ");
            }
        }

        if (sb.length() > 0) {
            sb.setLength(sb.length() - 2);
        }

        return sb.toString();
    }

    /**
     * Check options in long bits.
     *
     * @param options long option bits value.
     * @param check   current checked option.
     * @param more    more options.
     * @return        contains.
     */
    public static boolean checkOption(long options, Option check, Option... more) {
        long v1 = check.bits;
        for (Option option : more) {
            v1 |= option.bits;
        }

        return (v1 & options) == v1;
    }
}
